﻿namespace Centido.Core.Test
{
    using System.Data.Common;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Testing decimal table column
    /// </summary>
    [TestClass]
    public class DecimalColumnTest : ColumnBaseTest<decimal?>
    {
        /// <summary>
        /// Decimal table column
        /// </summary>
        private DecimalColumn Column
        {
            get { return GenericColumn as DecimalColumn; }
        }

        /// <summary>
        /// Get column type
        /// </summary>
        /// <returns></returns>
        protected override ColumnType GetColumnType()
        {
            return ColumnType.Decimal;
        }

        /// <summary>
        /// Get large value
        /// </summary>
        /// <returns></returns>
        protected override decimal? GetLargeValue()
        {
            return 1000M;
        }

        /// <summary>
        /// Get small data
        /// </summary>
        /// <returns></returns>
        protected override decimal? GetSmallValue()
        {
            return 1M;
        }

        /// <summary>
        /// Convert db value
        /// </summary>
        /// <param name="dbValue">Db value</param>
        /// <returns></returns>
        protected override decimal? ConvertDbValue(object dbValue)
        {
            return DbValue.GetDecimal(dbValue);
        }

        /// <summary>
        /// Reading and writing precision
        /// </summary>
        [TestMethod]
        public void ReadingAndWritingPrecision()
        {
            //Initially precision must be null
            Assert.IsNull(Column.Precision);

            //Setting presicion
            Column.Precision = 18;
            Column.Precision = 18;

            Assert.AreEqual(Column.Precision, 18);

            //Setting precision to null
            Column.Precision = null;
            Column.Precision = null;

            Assert.IsNull(Column.Precision);
        }

        /// <summary>
        /// Setting precision for deleted column
        /// </summary>
        [TestMethod]
        public void SettingPrecisionForDeletedColumn()
        {
            GenericColumn.Delete();

            try
            {
                Column.Precision = 5;
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.ColumnWasNotFound);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Writing large value to precision
        /// </summary>
        [TestMethod]
        public void WritingLargeValueToPrecision()
        {
            try
            {
                Column.Precision = int.MaxValue;
            }
            catch (DbException)
            {
                Assert.IsNull(Column.Precision);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Writing zero to precision
        /// </summary>
        [TestMethod]
        public void WritingZeroToPrecision()
        {
            try
            {
                Column.Precision = 0;
            }
            catch (DbException)
            {
                Assert.IsNull(Column.Precision);
                return;
            }

            Assert.Fail();
        }


        /// <summary>
        ///   Decreasion precision for an indexed column.
        /// </summary>
        [TestMethod]
        public void DecreasingPrecisionForIndexedColumn()
        {
            Column.Precision = 10;

            var index = Table.AddIndex(Column);

            Column.Precision = 5;

            Assert.AreEqual(Column.Precision, 5);
            Assert.IsTrue(index.Exists());
        }


        /// <summary>
        /// Inserting value larger than precision
        /// </summary>
        [TestMethod]
        public void InsertingValueLargerThanPrecision()
        {
            Column.Precision = 3;

            try
            {
                //Inserting value larger than precision
                Table.InsertRow(GenericColumn.Value(1245M));
            }
            catch (DbException)
            {
                Assert.AreEqual(Table.RowCount, 0);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Increasing precision after allowed value was inserted
        /// </summary>
        [TestMethod]
        public void IncreasingPrecisionAfterValueWasInserted()
        {
            //Setting precision
            Column.Precision = 1;

            //Inserting allowed value
            Table.InsertRow(GenericColumn.Value(1M));

            Assert.AreEqual(Table.RowCount, 1);

            //Increasing precision
            Column.Precision = 2;

            Assert.AreEqual(Column.Precision, 2);
        }

        /// <summary>
        /// Setting precision after not allowed value was inserted
        /// </summary>
        [TestMethod]
        public void DecreasingPrecisionAfterValueWasInserted()
        {
            //Setting precision
            Column.Precision = 2;

            //Inserting allowed value
            Table.InsertRow(GenericColumn.Value(15M));

            Assert.AreEqual(Table.RowCount, 1);

            try
            {
                //Decreasing precision
                Column.Precision = 1;
            }
            catch (DbException)
            {
                Assert.AreEqual(Column.Precision, 2);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// CHecking that digits after zero are not stored when scale is not specified
        /// </summary>
        [TestMethod]
        public void CheckingThatDigitsAfterZeroAreNotStoredWhenScaleIsNotSet()
        {
            Column.Precision = 5;

            //Inserting a value
            var rowId = Table.InsertRow(GenericColumn.Value(1.25M));

            Assert.AreEqual(Table.RowCount, 1);

            //Reading inserted value
            var inserted = GenericColumn.EmptyValue();

            Table.ReadRow(rowId, inserted);

            Assert.AreEqual(inserted.GetDecimal(), 1M);
        }

        /// <summary>
        /// Reading and writing scale
        /// </summary>
        [TestMethod]
        public void ReadingAndWritingScale()
        {
            //Initially scale must be null
            Assert.IsNull(Column.Scale);

            //Setting scale (without precision)
            Column.Scale = 5;
            Column.Scale = 5;

            Assert.AreEqual(Column.Scale, 5);

            //Setting scale to null
            Column.Scale = null;
            Column.Scale = null;

            Assert.IsNull(Column.Scale);
        }

        /// <summary>
        /// Setting scale for deleted column
        /// </summary>
        [TestMethod]
        public void SettingScaleForDeletedColumn()
        {
            GenericColumn.Delete();

            try
            {
                Column.Scale = 5;
            }
            catch (DbException e)
            {
                Assert.AreEqual(e.GetErrorCode(), DbErrorCode.ColumnWasNotFound);
                return;
            }

            Assert.Fail();
        }


        /// <summary>
        ///   Changing scale for an indexed column (bug fix).
        /// </summary>
        [TestMethod]
        public void ChangingScaleForIndexedColumn()
        {
            Column.Precision = 10;
            Column.Scale = 5;

            var index = Table.AddIndex(Column);

            Column.Scale = 2;

            Assert.AreEqual(Column.Scale, 2);

            Column.Scale = 7;

            Assert.AreEqual(Column.Scale, 7);
            Assert.IsTrue(index.Exists());
        }


        /// <summary>
        /// Writing zero to scale
        /// </summary>
        [TestMethod]
        public void WritingZeroToScale()
        {
            Column.Scale = 0;

            Assert.AreEqual(Column.Scale, 0);
        }

        /// <summary>
        /// Writing negative value to scale
        /// </summary>
        [TestMethod]
        public void WritingNegativeValueToScale()
        {
            try
            {
                Column.Scale = -1;
            }
            catch (DbException)
            {
                Assert.IsNull(Column.Scale);
                return;
            }

            Assert.Fail();
        }

        /// <summary>
        /// Setting scale equal to precision
        /// </summary>
        [TestMethod]
        public void SettingScaleEqualToPrecision()
        {
            Column.Scale = 1;

            Assert.AreEqual(Column.Scale, 1);

            Column.Precision = 1;

            Assert.AreEqual(Column.Precision, 1);
            Assert.AreEqual(Column.Scale, 1);
        }

        /// <summary>
        /// Setting scale greater than precision
        /// </summary>
        [TestMethod]
        public void SettingScaleGreaterThanPrecision()
        {
            Column.Scale = 2;

            Assert.AreEqual(Column.Scale, 2);

            try
            {
                Column.Precision = 1;
            }
            catch (DbException)
            {
                Assert.IsNull(Column.Precision);
                return;
            }

            Assert.Fail();
        }


        /// <summary>
        ///   Inserting a value that has digits after comma that is less than the scale.
        /// </summary>
        [TestMethod]
        public void InsertingValueLessThanScale()
        {
            Column.Precision = 10;
            Column.Scale = 4;

            var id = Table.InsertRow(Column.Value(34234.3245M));

            Assert.AreEqual(Column.Read(id).GetDecimal(), 34234.3245M);
        }


        /// <summary>
        /// Inserting value larger than scale
        /// </summary>
        [TestMethod]
        public void InsertingValueLargerThanScale()
        {
            //Setting precision and scale
            Column.Scale = 1;
            Column.Precision = 5;

            //Inserting value larger than scale
            var id = Table.InsertRow(GenericColumn.Value(1.27M));

            Assert.AreEqual(Table.RowCount, 1);

            Assert.AreEqual(Column.Read(id).GetDecimal(), 1.3M);
        }


        /// <summary>
        /// Checking that scale without precision does nothing
        /// </summary>
        [TestMethod]
        public void CheckingThatScaleWithoutPrecisionDoesNothing()
        {
            //Setting only scale
            Column.Scale = 5;

            //Inserting value larger than scale
            Table.InsertRow(GenericColumn.Value(1.2M));

            Assert.AreEqual(Table.RowCount, 1);
        }


        /// <summary>
        /// Changing scale after value was inserted
        /// </summary>
        [TestMethod]
        public void ChangingScaleAfterValueWasInserted()
        {
            //Setting scale first!!!
            Column.Scale = 1;
            Column.Precision = 5;

            //Inserting not allowed value
            Table.InsertRow(GenericColumn.Value(1.25M));

            try
            {
                //Increasing scale
                Column.Scale = 5;
            }
            catch (DbException)
            {
                Assert.AreEqual(Column.Scale, 1);
                return;
            }

            Assert.Fail();
        }


        /// <summary>
        ///   Setting non-integer default value (bug fix for Oracle).
        /// </summary>
        [TestMethod]
        public void SettingNonIntegerDefaultValue()
        {
            Column.DefaultValue = 12342342.44455M;

            Assert.AreEqual(Column.DefaultValue, 12342342.44455M);
        }


        /// <summary>
        ///   Setting non-integer maximum (bug fix for Oracle).
        /// </summary>
        [TestMethod]
        public void SettingNonIntegerMaximum()
        {
            Column.Maximum = 12342342.44455M;

            Assert.AreEqual(Column.Maximum, 12342342.44455M);
        }


        /// <summary>
        ///   Setting non-integer minimum (bug fix for Oracle).
        /// </summary>
        [TestMethod]
        public void SettingNonIntegerMinimum()
        {
            Column.Minimum = 12342342.44455M;

            Assert.AreEqual(Column.Minimum, 12342342.44455M);
        }


        /// <summary>
        ///   Inserting maximum decimal value.
        /// </summary>
        [TestMethod]
        public void InsertingMaxDecimalValue()
        {
            Column.Precision = 29;

            var id = Table.InsertRow(Column.Value(decimal.MaxValue));

            Assert.AreEqual(Column.Read(id).GetDecimal(), decimal.MaxValue);
        }


        /// <summary>
        ///   Inserting minimum decimal value.
        /// </summary>
        [TestMethod]
        public void InsertingMinDecimalValue()
        {
            Column.Precision = 29;

            var id = Table.InsertRow(Column.Value(decimal.MinValue));

            Assert.AreEqual(Column.Read(id).GetDecimal(), decimal.MinValue);
        }
    }
}